package com.harry.decoupling.impl.tool;

import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.Date;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.harry.decoupling.defi.proxy.GenericSrvProxy;
import com.harry.decoupling.defi.tool.DataExchange;
import com.harry.decoupling.defi.tool.NotificationManager;
import com.harry.decoupling.defi.tool.ResourceContextProcessor;
import com.harry.decoupling.defi.tool.TemplateManager;
import com.harry.decoupling.defi.tool.ThreadPoolManager;
import com.harry.resource.inner.entity.InnerNotificationRecord;
import com.harry.resource.inner.entity.InnerUser;
import com.harry.utility.InnerUtils;
import com.harry.utility.PoEntityHelper;
import com.harry.utility.query.InnerCriteria;

@Component
public class DefaultNotificationManager implements NotificationManager {

	@Autowired
	TemplateManager tpltMgr;

	@Autowired
	GenericSrvProxy srv;

	@Autowired
	DataExchange de;

	@Autowired
	ThreadPoolManager tpm;

	@Autowired
	ResourceContextProcessor rcp;

	@Value("${sendgrid.connection.api_key}")
	String api_key;

	@Value("${sendgrid.mail.sender.email}")
	String senderEmail;

	@Value("${sendgrid.mail.sender.name}")
	String senderName;

	@Override
	public void push(InnerNotificationRecord notification) throws Exception {
		InnerUser currentUser = InnerUtils.getUser();
		Map<String, Object> ctx = PoEntityHelper.apiContext();
		tpm.submit(() -> {
			PoEntityHelper.apiContext(ctx, () -> InnerUtils.setUser(currentUser, () -> {
				InnerUtils.trySuccess(() -> {
					this.__push(notification);
				}, () -> {
					srv.updateByCriteria(InnerNotificationRecord.class// e
					, InnerUtils.mapObject("pushSuccess", true, "pushTime", new Date())// e
					, new InnerCriteria("eq", "id", notification.getId()));
					return null;
				}, e -> {
					srv.updateByCriteria(InnerNotificationRecord.class// e
					, InnerUtils.mapObject("pushSuccess", false, "pushTime", new Date(), "failReason", e.getMessage())// e
					, new InnerCriteria("eq", "id", notification.getId()));
					e.printStackTrace();
					return null;
				});
			}));
		});
	}

	public void __push(InnerNotificationRecord notification) throws Exception {
		if ("01".equals(notification.getType())) {
			// template
			String notiActString = (String) PoEntityHelper.readValueByProperty(notification, "variables");
			Object[] recipients = PoEntityHelper.toObjectArray(notification.getRecipients());
			String templateId = notification.getTemplate().getId();
			for (int i = 0; i < recipients.length; i++) {
				Object[] notiActions = StringUtils.isEmpty(notiActString) ? new Object[] {} : de.parse(notiActString, Object[].class);
				Map<String, Object> context = InnerUtils.mapObject();
				context.putAll(PoEntityHelper.apiContext());
				context.putAll(InnerUtils.mapObject("recipient", recipients[i], "notification", notification));
				rcp.process(notiActions, context);
				// writer
				StringWriter writer = new StringWriter();
				// print
				tpltMgr.print(templateId, context, writer);
				// get the result and send email
				sendEmail((InnerUser) recipients[i], notification.getName(), writer.getBuffer());
			}
		}
	}

	private void sendEmail(InnerUser recipient, String name, StringBuffer buffer) throws Exception {
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpPost httpPost = new HttpPost("https://api.sendgrid.com/v3/mail/send");
		RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(20000).setConnectionRequestTimeout(10000).build();
		httpPost.setConfig(requestConfig);
		httpPost.addHeader("content-type", "application/json;charset=UTF-8");
		httpPost.addHeader("Authorization", "Bearer " + api_key);
		Map<String, Object> data = InnerUtils.mapObject();
		PoEntityHelper.writeValueByProperty(data, "personalizations", new Object[] { InnerUtils.mapObject(null, null// e
				, "subject", emailSubjectConvert(name)// e
				, "to", new Object[] { InnerUtils.mapObject("email", recipient.getUsername()) }// e
				) });
		PoEntityHelper.writeValueByProperty(data, "from.email", senderEmail);
		PoEntityHelper.writeValueByProperty(data, "from.name", senderEmail);
		PoEntityHelper.writeValueByProperty(data, "content[0]type", "text/html");
		PoEntityHelper.writeValueByProperty(data, "content[0]value", buffer.toString());
		httpPost.setEntity(new StringEntity(de.serialize(data)));
		CloseableHttpResponse response = null;
		try {
			response = httpClient.execute(httpPost);
			String result = org.apache.http.util.EntityUtils.toString(response.getEntity());
			if (!"".equals(result)) {
				throw new Exception(result);
			}
		} finally {
			InnerUtils.closeClosable(response, httpClient);
		}
	}

	private String emailSubjectConvert(String subject) throws UnsupportedEncodingException {
		String charset = "UTF8";
		return emailSubjectConvert(subject, charset);
	}

	private String emailSubjectConvert(String subject, String charset) throws UnsupportedEncodingException {
		subject = new String(subject.getBytes(), charset);
		subject = new String(Base64.getEncoder().encode((subject).getBytes()));
		subject = "=?" + charset + "?B?" + subject + "?=";
		return subject;
	}
}
